/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package parser.analyzer

import java.io.File
import java.io.IOException
import com.google.protobuf.Descriptors._
import com.google.protobuf._
import scala.io.Source
import scala.util.parsing.input.Position
import scala.concurrent._
import scala.concurrent.ops._
import scala.collection.JavaConversions.asBuffer
import util.PBUtils._
import scala.util.control.Exception._

import parser.Results._
import parser.Messages

/**
 * ProtocolBuffer IDL reader.
 */
object PBReader {

  /**
   * Reads ProtocolBuffer IDL.
   *
   * @throws IOException
   * @throws PBConvertingException
   */
  def read(pos: Position, protoFile: String): Result[List[FileDescriptor]] = {

    def result[T](f: => T) = {
      allCatch.either(f) match {
        case Left(e) => failure(pos, e.toString)
        case Right(v) => Success(v)
      }
    }

    val f = new File(protoFile).getAbsoluteFile
    
    result(new ProcessBuilder("protoc", "-o/dev/stdout", "-I", f.getParent, f.getPath).start()).flatMap { process =>
      val os = process.getInputStream
      val es = Source.fromInputStream(process.getErrorStream)

      val descFuture = future {
        DescriptorProtos.FileDescriptorSet.parseFrom(os)
      }
      val errorsFuture = future { 
        es.getLines.toList
      }

      val msg = allCatch.either{errorsFuture()} match {
        case Left(e)       => ": Exception occurs:" + e.toString()
        case Right(Nil)    => ""
        case Right(errors) => ": protoc print the following messages to stderr:\n\t" + errors.mkString("\n\t")
      }

      val exitCode = process.waitFor()
      if (exitCode != 0) {
        failure(pos, "protoc failed" + msg)
      } else {
          val msgs = if (msg.nonEmpty) List(Messages.warning(pos,  msg))
                     else              Nil
        Success(Unit, msgs).flatMap { _ =>
          result(descFuture()).map { desc =>
            convertFiles(desc).values.toList
          }
        }
      }
    }
  }
}
